import java.util.ArrayList;
import java.util.List;
import java.util.Random;


public abstract class Neuron {
	protected static int neighbourhoodRadius = 1;
	protected static int neighbourhoodDimension = 0;
	protected static float alpha = 0.5f; //wspolczynnik uczenia
	
	protected float threshold = 0.0f;
	protected float output;
	protected float input;
	protected List<Float> weights = new ArrayList<Float>();
	protected float error;
	
	protected NeuronLayer blayer;

	public Neuron(String weights) {
		String[] weightsTab = weights.split(" ");
		for(String weight : weightsTab) {
			this.weights.add(new Float(weight));
		}
		//System.out.println("\tdodano neuron o wejsciach "+this.weights);
	}
	
	public Neuron(int weightsNum) {
		Random random = new Random();
		for(int i=0;i<weightsNum;i++){
			this.weights.add(random.nextFloat());
		}
		//System.out.println("added neuron with weights "+this.weights);
	}
	
	public Neuron(){}
	
	public void addWeight(Float weight) {
		this.weights.add(weight);
	}
	
	public void setWeight(int index, Float w) {
		this.weights.set(index, w<0.0001f ? 0.0f : w);
	}
	
	public void setOutput(float output) {
		this.output = output;
	}
	
	public void setThreshold(Float threshold) {
		this.threshold = threshold;
	}
	
	public Float getThreshold() {
		return this.threshold;
	}
	
	public Float getWeight(int index) {
		return this.weights.get(index);
	}
	
	public List<Float> getWeights() {
		List<Float> res = new ArrayList<Float>();
		for(Float w : this.weights) {
			res.add(w<0.0001f ? 0.0f : w);
		}
		return this.weights;
	}
	
	public void handleSignal(NeuronLayer layer) {
		//System.out.println("handle for neuron "+this.weights);
		computeInput(layer);
		//System.out.println("my input "+this.input);
		computeOutput();
		//System.out.println("n handle signal done, my output: "+this.output);
	}
	
	public abstract void computeOutput();
	
	public float getOutput() {
		return this.output;
	}
	
	public void computeInput(NeuronLayer layer) {
		this.blayer = layer;
		float inputSignal = 0;
		if(layer.type == NeuronLayer.KOHONEN) {
			((KohonenLayer)layer).normalizeForWidrow();
		}
		List<Neuron> neurons = layer.getNeurons();
		for(int i=0; i<neurons.size(); i++) {
			inputSignal += this.weights.get(i)*neurons.get(i).getOutput();
		}
		//System.out.println("n computed input signal: "+this.input);
		this.input = inputSignal;
	}
	
	public abstract void learn(NeuronLayer input, Float neighbourhood);

	public void setWeights(String weights) {
		this.weights.clear();
		for(String weight : weights.split("\\s+")) {
			this.weights.add(new Float(weight));
		}
	}

	public void setWeightsAndThreshold(String weights) {
		this.weights.clear();
		String[] data = weights.split("\\s+");
		for(int i=0;i<data.length-1;i++) {
			this.weights.add(new Float(data[i]));
		}
		this.setThreshold(new Float(data[data.length-1]));
	}

	public float getError() {
		return error;
	}

	public void setError(float error) {
		this.error = error;
	}
	
	public abstract float derivative();
}
